﻿/**
 *	@file		CCEGLView.cpp
 *	@author		NeoZhang
 *	@version	v0.1
 *	@date		2013/09/03
 *	@brief		
 */


#include "CCEGLView.h"
#include "GL\glew.h"

NS_CC_BEGIN

/** 
 *	定义编译器版本
 *
 *	Visual Studio 2010 or higher version
 */
#if(_MSC_VER >= 1600)

//! Windows触摸定义
#define MOUSEEVENTF_FROMTOUCH 0xFF515700

//!	Windows触摸函数
//!	使程序可以在Windows XP上使用
typedef	WINUSERAPI BOOL (WINAPI *RegisterTouchWindowFn)(_In_ HWND hwnd, _In_ ULONG ulFlags);
typedef WINUSERAPI BOOL (WINAPI *UnregisterTouchWindowFn)(_In_ HWND hwnd);
typedef WINUSERAPI LPARAM (WINAPI *GetMessageExtraInfoFn)(VOID);
typedef WINUSERAPI BOOL (WINAPI *GetTouchInputInfoFn)(_In_ HTOUCHINPUT hTouchInput, _In_ UINT cInputs, __out_ecount(cInputs) PTOUCHINPUT pInputs, _In_ int cbSize);
typedef WINUSERAPI BOOL (WINAPI *CloseTouchInputHandleFn)(_In_ HTOUCHINPUT hTouchInput);

static RegisterTouchWindowFn s_pfRegisterTouchWindowFunction = NULL;
static UnregisterTouchWindowFn s_pfUnregisterTouchWindowFunction = NULL;
static GetMessageExtraInfoFn s_pfGetMessageExtraInfoFunction = NULL;
static GetTouchInputInfoFn s_pfGetTouchInputInfoFunction = NULL;
static CloseTouchInputHandleFn s_pfCloseTouchInputHandleFunction = NULL;

static bool CheckTouchSupport()
{
	s_pfRegisterTouchWindowFunction = (RegisterTouchWindowFn)GetProcAddress(GetModuleHandle(TEXT("user32.dll")), "RegisterTouchWindow");
	s_pfUnregisterTouchWindowFunction = (UnregisterTouchWindowFn)GetProcAddress(GetModuleHandle(TEXT("user32.dll")), "UnregisterTouchWindow");
	s_pfGetMessageExtraInfoFunction = (GetMessageExtraInfoFn)GetProcAddress(GetModuleHandle(TEXT("user32.dll")), "GetMessageExtraInfo");
	s_pfGetTouchInputInfoFunction = (GetTouchInputInfoFn)GetProcAddress(GetModuleHandle(TEXT("user32.dll")), "GetTouchInputInfo");
	s_pfCloseTouchInputHandleFunction = (CloseTouchInputHandleFn)GetProcAddress(GetModuleHandle(TEXT("user32.dll")), "CloseTouchInputHandle");

	return (s_pfRegisterTouchWindowFunction && s_pfUnregisterTouchWindowFunction && s_pfGetMessageExtraInfoFunction && s_pfGetTouchInputInfoFunction && s_pfCloseTouchInputHandleFunction);
}

#endif
//!	以上部分可以不用关心

static void SetupPixelFormat(HDC hDC)
{
    int pixelFormat;

    PIXELFORMATDESCRIPTOR pfd =
    {
        sizeof(PIXELFORMATDESCRIPTOR),  // size
        1,                          // version
        PFD_SUPPORT_OPENGL |        // OpenGL window
        PFD_DRAW_TO_WINDOW |        // render to window
        PFD_DOUBLEBUFFER,           // support double-buffering
        PFD_TYPE_RGBA,              // color type
        32,                         // preferred color depth
        0, 0, 0, 0, 0, 0,           // color bits (ignored)
        0,                          // no alpha buffer
        0,                          // alpha bits (ignored)
        0,                          // no accumulation buffer
        0, 0, 0, 0,                 // accum bits (ignored)
        24,                         // depth buffer
        8,                          // no stencil buffer
        0,                          // no auxiliary buffers
        PFD_MAIN_PLANE,             // main layer
        0,                          // reserved
        0, 0, 0,                    // no layer, visible, damage masks
    };

    pixelFormat = ChoosePixelFormat(hDC, &pfd);
    SetPixelFormat(hDC, pixelFormat, &pfd);
}

static bool glew_dynamic_binding()
{
	const char *gl_extensions = (const char*)glGetString(GL_EXTENSIONS);

	// If the current opengl driver doesn't have framebuffers methods, check if an extension exists
	if (glGenFramebuffers == NULL)
	{
		//CCLog("OpenGL: glGenFramebuffers is NULL, try to detect an extension\n");
		if (strstr(gl_extensions, "ARB_framebuffer_object"))
		{
			//CCLog("OpenGL: ARB_framebuffer_object is supported\n");

			glIsRenderbuffer = (PFNGLISRENDERBUFFERPROC) wglGetProcAddress("glIsRenderbuffer");
			glBindRenderbuffer = (PFNGLBINDRENDERBUFFERPROC) wglGetProcAddress("glBindRenderbuffer");
			glDeleteRenderbuffers = (PFNGLDELETERENDERBUFFERSPROC) wglGetProcAddress("glDeleteRenderbuffers");
			glGenRenderbuffers = (PFNGLGENRENDERBUFFERSPROC) wglGetProcAddress("glGenRenderbuffers");
			glRenderbufferStorage = (PFNGLRENDERBUFFERSTORAGEPROC) wglGetProcAddress("glRenderbufferStorage");
			glGetRenderbufferParameteriv = (PFNGLGETRENDERBUFFERPARAMETERIVPROC) wglGetProcAddress("glGetRenderbufferParameteriv");
			glIsFramebuffer = (PFNGLISFRAMEBUFFERPROC) wglGetProcAddress("glIsFramebuffer");
			glBindFramebuffer = (PFNGLBINDFRAMEBUFFERPROC) wglGetProcAddress("glBindFramebuffer");
			glDeleteFramebuffers = (PFNGLDELETEFRAMEBUFFERSPROC) wglGetProcAddress("glDeleteFramebuffers");
			glGenFramebuffers = (PFNGLGENFRAMEBUFFERSPROC) wglGetProcAddress("glGenFramebuffers");
			glCheckFramebufferStatus = (PFNGLCHECKFRAMEBUFFERSTATUSPROC) wglGetProcAddress("glCheckFramebufferStatus");
			glFramebufferTexture1D = (PFNGLFRAMEBUFFERTEXTURE1DPROC) wglGetProcAddress("glFramebufferTexture1D");
			glFramebufferTexture2D = (PFNGLFRAMEBUFFERTEXTURE2DPROC) wglGetProcAddress("glFramebufferTexture2D");
			glFramebufferTexture3D = (PFNGLFRAMEBUFFERTEXTURE3DPROC) wglGetProcAddress("glFramebufferTexture3D");
			glFramebufferRenderbuffer = (PFNGLFRAMEBUFFERRENDERBUFFERPROC) wglGetProcAddress("glFramebufferRenderbuffer");
			glGetFramebufferAttachmentParameteriv = (PFNGLGETFRAMEBUFFERATTACHMENTPARAMETERIVPROC) wglGetProcAddress("glGetFramebufferAttachmentParameteriv");
			glGenerateMipmap = (PFNGLGENERATEMIPMAPPROC) wglGetProcAddress("glGenerateMipmap");
		}
		else
		if (strstr(gl_extensions, "EXT_framebuffer_object"))
		{
			//CCLog("OpenGL: EXT_framebuffer_object is supported\n");
			glIsRenderbuffer = (PFNGLISRENDERBUFFERPROC) wglGetProcAddress("glIsRenderbufferEXT");
			glBindRenderbuffer = (PFNGLBINDRENDERBUFFERPROC) wglGetProcAddress("glBindRenderbufferEXT");
			glDeleteRenderbuffers = (PFNGLDELETERENDERBUFFERSPROC) wglGetProcAddress("glDeleteRenderbuffersEXT");
			glGenRenderbuffers = (PFNGLGENRENDERBUFFERSPROC) wglGetProcAddress("glGenRenderbuffersEXT");
			glRenderbufferStorage = (PFNGLRENDERBUFFERSTORAGEPROC) wglGetProcAddress("glRenderbufferStorageEXT");
			glGetRenderbufferParameteriv = (PFNGLGETRENDERBUFFERPARAMETERIVPROC) wglGetProcAddress("glGetRenderbufferParameterivEXT");
			glIsFramebuffer = (PFNGLISFRAMEBUFFERPROC) wglGetProcAddress("glIsFramebufferEXT");
			glBindFramebuffer = (PFNGLBINDFRAMEBUFFERPROC) wglGetProcAddress("glBindFramebufferEXT");
			glDeleteFramebuffers = (PFNGLDELETEFRAMEBUFFERSPROC) wglGetProcAddress("glDeleteFramebuffersEXT");
			glGenFramebuffers = (PFNGLGENFRAMEBUFFERSPROC) wglGetProcAddress("glGenFramebuffersEXT");
			glCheckFramebufferStatus = (PFNGLCHECKFRAMEBUFFERSTATUSPROC) wglGetProcAddress("glCheckFramebufferStatusEXT");
			glFramebufferTexture1D = (PFNGLFRAMEBUFFERTEXTURE1DPROC) wglGetProcAddress("glFramebufferTexture1DEXT");
			glFramebufferTexture2D = (PFNGLFRAMEBUFFERTEXTURE2DPROC) wglGetProcAddress("glFramebufferTexture2DEXT");
			glFramebufferTexture3D = (PFNGLFRAMEBUFFERTEXTURE3DPROC) wglGetProcAddress("glFramebufferTexture3DEXT");
			glFramebufferRenderbuffer = (PFNGLFRAMEBUFFERRENDERBUFFERPROC) wglGetProcAddress("glFramebufferRenderbufferEXT");
			glGetFramebufferAttachmentParameteriv = (PFNGLGETFRAMEBUFFERATTACHMENTPARAMETERIVPROC) wglGetProcAddress("glGetFramebufferAttachmentParameterivEXT");
			glGenerateMipmap = (PFNGLGENERATEMIPMAPPROC) wglGetProcAddress("glGenerateMipmapEXT");
		}
		else
		{
			//CCLog("OpenGL: No framebuffers extension is supported\n");
			//CCLog("OpenGL: Any call to Fbo will crash!\n");
			return false;
		}
	}
	return true;
}

//////////////////////////////////////////
//	CCEGLView实现
/////////////////////////////////////////

static CCEGLView* s_pMainWindow = NULL;
static const WCHAR* kWindowClassName = L"Cocos2dxWin32";

/**
 *	定义窗口过程
 *
 *	操作系统向应用程序发送一系列消息，如左键按下和左键弹起
 *	应用程序将通过GetMessage等方法最终将消息提交到窗口过程
 *	指向一个应用程序定义的窗口过程的指针。
 *
 *	@param HWND : 窗口句柄(Window Handle)
 *	@param uMsg : 消息ID(Message ID)
 *	@param wParam :	消息参数
 *	@param LParam : 消息参数
 */
static LRESULT CALLBACK _WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
	if(s_pMainWindow && s_pMainWindow->getHWnd() == hWnd)
	{
		return s_pMainWindow->WindowProc(uMsg, wParam, lParam);
	}
	else
	{
		return DefWindowProc(hWnd, uMsg, wParam, lParam);
	}
}

//!	构造函数
CCEGLView::CCEGLView()
:m_bCaptured(false)
,m_hWnd(NULL)
,m_hDC(NULL)
,m_hRC(NULL)
,m_menu(NULL)
,m_wndproc(NULL)
,m_fFrameZoomFactor(1.0f)
,m_bSupportTouch(false)
{
	strcpy(m_szViewName, "Cocos2dxWin32");
}

//!	析构函数
CCEGLView::~CCEGLView()
{
}

bool CCEGLView::initGL()
{
	m_hDC = GetDC(m_hWnd);
	SetupPixelFormat(m_hDC);
	m_hRC = wglCreateContext(m_hDC);
	wglMakeCurrent(m_hDC, m_hRC);

	const GLubyte* glVersion = glGetString(GL_VERSION);
	//CCLOG("OpenGL version = %s", glVersion);

	if(atof((const char*)glVersion) < 1.5)
	{
		char strComplain[256] = {0};
		sprintf(strComplain,
		"OpenGL 1.5 or higher is required (your version is %s). Please upgrade the driver of your video card.",
		glVersion);
		//CCMessageBox(strComplain, "OpenGL version too old");
		return false;
	}
	GLenum GlewInitResult = glewInit();
    if (GLEW_OK != GlewInitResult)
    {
		//CCMessageBox((char *)glewGetErrorString(GlewInitResult), "OpenGL error");
        return false;
    }

    if (GLEW_ARB_vertex_shader && GLEW_ARB_fragment_shader)
    {
        //CCLog("Ready for GLSL");
    }
    else
    {
        //CCLog("Not totally ready :(");
    }

    if (glewIsSupported("GL_VERSION_2_0"))
    {
        //CCLog("Ready for OpenGL 2.0");
    }
    else
    {
        //CCLog("OpenGL 2.0 not supported");
    }

	if(glew_dynamic_binding() == false)
	{
		//CCMessageBox("No OpenGL framebuffer support. Please upgrade the driver of your video card.", "OpenGL error");
		return false;
	}

    // Enable point size by default on windows.
    glEnable(GL_VERTEX_PROGRAM_POINT_SIZE);

	return true;
}

void CCEGLView::destroyGL()
{
    if (m_hDC != NULL && m_hRC != NULL)
    {
        // deselect rendering context and delete it
        wglMakeCurrent(m_hDC, NULL);
        wglDeleteContext(m_hRC);
    }
}

HWND CCEGLView::getHWnd()
{
	return m_hWnd;
}

//! OpenGL窗口创建函数
bool CCEGLView::create()
{
	/**
	 *	初始化返回指令
	 *	
	 *	@default false
	 */
	bool bRet = false;
	do
	{

		CC_BREAK_IF(m_hWnd);

		HINSTANCE hInstance = GetModuleHandle(NULL);
		//!	窗口类结构
		WNDCLASS wc;

		//!	定义窗口类的风格
		wc.style			= CS_HREDRAW |	CS_VREDRAW | CS_OWNDC;
		//!	窗口过程
		wc.lpfnWndProc		= _WindowProc;
		//!	No Extra Window Data
		wc.cbClsExtra		= 0;
		//!	No Extra Window Data
		wc.cbWndExtra		= 0;
		//!	设置hInstance
		wc.hInstance		= hInstance;
		//!	读取默认Icon
		wc.hIcon			= LoadIcon(NULL, IDI_WINLOGO);
		//!	读取箭头指针
		wc.hCursor			= LoadCursor(NULL, IDC_ARROW);
		//!	没有背景
		wc.hbrBackground	= NULL;
		//!	设置菜单名称
		wc.lpszMenuName		= m_menu;
		//!	设置类名
		wc.lpszClassName	= kWindowClassName;

		CC_BREAK_IF(! RegisterClass(&wc) && 1410 != GetLastError());

		//! 窗口中心位置
		RECT rcDesktop;
		//!	获取Windows屏幕分辨率
		GetWindowRect(GetDesktopWindow(), &rcDesktop);

		WCHAR wszBuf[50] = {0};
		MultiByteToWideChar(CP_UTF8, 0, m_szViewName, -1, wszBuf, sizeof(wszBuf));

		//! 创建窗口
		m_hWnd = CreateWindowEx (
			//!	窗口扩展样式
			WS_EX_APPWINDOW | WS_EX_WINDOWEDGE,
			//!	窗口类名
			kWindowClassName,
			//!	窗口标题
			wszBuf,
			//! 默认窗口样式
			WS_CAPTION | WS_POPUPWINDOW | WS_MINIMIZEBOX,
			//!	窗口位置
			0, 0,
			//!	窗口宽
			//!	使用一个大值来初始宽以避免"GetClientRect"获得一个错误的客户端区域信息
			1000,
			//!	窗口高
			1000,
			//!	没有父窗口
			NULL,
			//!	没有菜单
			NULL,
			hInstance,
			NULL);

		CC_BREAK_IF(!m_hWnd);

		//! 初始化opengl
		bRet = initGL();
		//!	如果初始化失败，则消除opengl
		if(!bRet) destroyGL();
		//!	如果初始化失败，则break
		CC_BREAK_IF(!bRet);

		s_pMainWindow = this;
		bRet = true;
	}while (0);

#if(_MSC_VER > 1600)
	m_bSupportTouch = CheckTouchSupport();
	if(m_bSupportTouch)
	{
		m_bSupportTouch = (s_pfRegisterTouchWindowFunction(m_hWnd, 0) != 0);
	}
#endif
	return bRet;
}

LRESULT CCEGLView::WindowProc(UINT message, WPARAM wParam, LPARAM lParam)
{
	BOOL bProccessed = FALSE;

	switch (message)
	{
	case WM_LBUTTONDOWN:
#if(_MSC_VER >= 1600)
		if(m_bSupportTouch && (s_pfGetMessageExtraInfoFunction() & MOUSEEVENTF_FROMTOUCH) == MOUSEEVENTF_FROMTOUCH) break;
#endif

		if(/*m_pDelegate &&*/ MK_LBUTTON == wParam)
		{
			POINT point = {(short)LOWORD(lParam), (short)HIWORD(lParam)};
			CCPoint pt(point.x, point.y);
			pt.x /= m_fFrameZoomFactor;
			pt.y /= m_fFrameZoomFactor;
			CCPoint tmp = ccp(pt.x, m_obScreenSize.height - pt.y);
			if(m_obViewPortRect.equals(CCRectZero) || m_obViewPortRect.containsPoint(tmp))
			{
				m_bCaptured = true;
				SetCapture(m_hWnd);
				int id = 0;
				//handleTouchesBegin();
			}
		}
		break;
	case WM_PAINT:
		PAINTSTRUCT ps;
		BeginPaint(m_hWnd, &ps);
		EndPaint(m_hWnd, &ps);
		break;

	case WM_DESTROY:
		destroyGL();
		PostQuitMessage(0);
		break;

	default:
		if(m_wndproc)
		{
			m_wndproc(message, wParam, lParam, &bProccessed);
			if(bProccessed) break;
		}
		return DefWindowProc(m_hWnd, message, wParam, lParam);
	}
	if (m_wndproc && !bProccessed)
	{
		m_wndproc(message, wParam, lParam, &bProccessed);
	}
	return 0;
}

//! 父类CCEGLViewProtocol的end()方法实现
//!	强制销毁opengl视图
void CCEGLView::end()
{
	if(m_hWnd)
	{
#if(_MSC_VER >= 1600)
		if(m_bSupportTouch)
		{
			s_pfUnregisterTouchWindowFunction(m_hWnd);
		}
#endif
		DestroyWindow(m_hWnd);
		m_hWnd = NULL;
	}
	s_pMainWindow = NULL;
	UnregisterClass(kWindowClassName, GetModuleHandle(NULL));
	delete this;
}

//!	判断opengl渲染器是否准备好
bool CCEGLView::isOpenGLReady()
{
	return (m_hDC != NULL && m_hRC != NULL);
}

//!	交换前后缓冲区
void CCEGLView::swapBuffers()
{
	if(m_hDC != NULL)
	{
		::SwapBuffers(m_hDC);
	}
}

//!	打开或关闭输入法键盘
void CCEGLView::setIMEKeyboardState(bool)
{

}

//!	共享OpenGL窗口
CCEGLView*	CCEGLView::sharedOpenGLView()
{
	static CCEGLView* s_pEglView = NULL;
	if(s_pEglView == NULL)
	{
		s_pEglView = new CCEGLView();
		if(s_pEglView && s_pEglView->create())
		{
			delete s_pEglView;
			s_pEglView = NULL;
		}
	}
	return s_pEglView;
}

NS_CC_END